home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2002 #11 / Amiga Plus CD - 2002 - No. 11.iso / Tools / Freeware / sysmon / src / sysinfo / SysInfo.c < prev    next >
C/C++ Source or Header  |  2002-10-27  |  12KB  |  344 lines

  1. /*
  2. **    $RCSfile: sysinfo.c,v $
  3. **    $Filename: sysinfo.c $
  4. **    $Revision: 1.0 $
  5. **    $Date: 1996/08/20 15:35:37 $
  6. **
  7. **    sysinfo.library interface to sysmon.library features (version 2.2) 
  8. **    See the SysInfo.doc autodoc file in the Executive distribution
  9. **    for more information about the available function calls.
  10. **
  11. **    (C) Copyright 1995-2001 by Etienne Vogt <Etienne.Vogt@obspm.fr>
  12. **    SysInfo API by Petri Nordlund <petrin@megabaud.fi>
  13. */
  14.  
  15. #include <exec/libraries.h>
  16. #include <exec/memory.h>
  17. #include <exec/tasks.h>
  18. #include <exec/alerts.h>
  19. #include <exec/execbase.h>
  20. #include <devices/timer.h>
  21. #define __USE_SYSBASE
  22. #include <proto/exec.h>
  23. #include <proto/timer.h>
  24. #include <clib/alib_protos.h>
  25. #include "/sysmon.h"
  26. #include "/sysmon_protos.h"
  27. #include "/sysmon_pragmas.h"
  28. #include "sysinfo.h"
  29.  
  30. #define CPU_USAGE_RECENT 60    /* Recent CPU Usage is last minute */
  31.  
  32. static volatile ULONG RecentCPUUsed[CPU_USAGE_RECENT];
  33. static volatile int LastRecentCPU = 0;
  34. static volatile ULONG LastSecDispCount, LastSecVolTSw, LastSecInvTSw;
  35.  
  36. struct SysmonBase *SysmonBase = NULL;
  37. struct Device *TimerBase = NULL;
  38. struct ExecBase *SysBase = NULL;
  39. static struct timerequest timereq;
  40. static BOOL ServerRunning = FALSE, ServerExit = FALSE;
  41. static struct Task *ServerTask;
  42. extern __far void ServerEntry(void);    /* Stubs for server task */
  43. extern __far struct Library *SysInfoBase;
  44.  
  45. int __saveds __asm __UserLibInit(register __a6 struct Library *libbase);
  46. int __saveds __asm __UserLibCleanup(register __a6 struct Library *libbase);
  47. static struct EClockVal subEClockVal(struct EClockVal e2, struct EClockVal e1);
  48. static __inline struct EClockVal addEClockVal(struct EClockVal e2, struct EClockVal e1);
  49. void __saveds SysInfoServer(void);
  50.  
  51.  
  52. /* This function is executed by ramlib as part of the library initialization process.
  53.    We open sysmon.library, timer.device and start the server task via an assembler
  54.    stub routine as CreateTask() does not allow us to preload A6 with the library
  55.    base (and AddTask() is just a pain to use directly). */
  56.  
  57. int __saveds __asm __UserLibInit(register __a6 struct Library *libbase)
  58. { int rc = 0;
  59.  
  60.   SysBase = *(struct ExecBase **)4;
  61.   if (SysmonBase = (struct SysmonBase *)OpenLibrary("sysmon.library",1))
  62.   { if (OpenDevice("timer.device",UNIT_VBLANK,(struct IORequest *)&timereq,0) == 0)
  63.     { TimerBase = timereq.tr_node.io_Device;
  64.       Forbid();
  65.       SysInfoBase = libbase;
  66.       if (ServerTask = CreateTask("SysInfo.server",18,ServerEntry,4000))
  67.       { ServerRunning = TRUE;
  68.     ServerTask->tc_UserData = libbase;
  69.       }
  70.       else rc = 1;
  71.       Permit();
  72.     }
  73.     else
  74.     { CloseLibrary((struct Library *)SysmonBase);
  75.       Alert(AT_Recovery|AG_OpenDev|AO_TimerDev);
  76.       rc = 1;
  77.     }
  78.   }
  79.   else rc = 1;
  80.  
  81.   return rc;
  82. }
  83.  
  84.  
  85. /* This routine is called by ramlib's low memory handler on a failed memory allocation
  86.    request if our library is no longer opened. Since we can't task switch from a low mem
  87.    handler, we notify the server task that it should exit as soon as possible and delay
  88.    the expunge. The task will call this again on exit to actually flush the library out. */
  89.  
  90. int __saveds __asm __UserLibCleanup(register __a6 struct Library *libbase)
  91. { if (ServerRunning)    /* If server is running, tell it to exit and delay the expunge */
  92.   { ServerExit = TRUE;
  93.     return 1;
  94.   }
  95.   else
  96.   { if (TimerBase) CloseDevice((struct IORequest *)&timereq);
  97.     if (SysmonBase) CloseLibrary((struct Library *)SysmonBase);
  98.     return 0;
  99.   }
  100. }
  101.  
  102.  
  103. */ This function is used to query the library about its supported features */
  104.  
  105. __saveds struct SysInfo *InitSysInfo(void)
  106. { struct SysInfo *si;
  107.  
  108.   if (si = AllocMem(sizeof(struct SysInfo), MEMF_PUBLIC | MEMF_CLEAR))
  109.   { si->loadavg_type = LOADAVG_FIXEDPNT;
  110.     si->loadavg_time1 = 60;
  111.     si->loadavg_time2 = 5*60;
  112.     si->loadavg_time3 = 15*60;
  113.     si->fscale = 60;
  114.     si->cpu_usage_implemented = CPU_USAGEF_TOTAL_IMPLEMENTED |
  115.                 CPU_USAGEF_LASTSEC_IMPLEMENTED | CPU_USAGEF_RECENT_IMPLEMENTED |
  116.                 CPU_USAGEF_TOTALCSW_IMPLEMENTED | CPU_USAGEF_IVVOCSW_IMPLEMENTED |
  117.                 CPU_USAGEF_IVVOCSW_LASTSEC_IMPLEMENTED | CPU_USAGEF_TOTALCSW_LASTSEC_IMPLEMENTED;
  118.     si->task_cpu_usage_implemented = TASK_CPU_USAGEF_TOTAL_IMPLEMENTED |
  119.                 TASK_CPU_USAGEF_TOTALCSW_IMPLEMENTED | TASK_CPU_USAGEF_IVVOCSW_IMPLEMENTED;
  120.   }
  121.  
  122.   return si;
  123. }
  124.  
  125.  
  126. */ The function cleans up whatever was allocated by InitSysInfo() */
  127.  
  128. __asm __saveds void FreeSysInfo(register __a0 struct SysInfo *si)
  129. { if (si) FreeMem(si, sizeof(struct SysInfo));
  130. }
  131.  
  132.  
  133. */ This function computes Load Averages from the data collected by sysmon.library's
  134.    VBLANK interrupt server */
  135.  
  136. __asm __saveds void GetLoadAverage(register __a0 struct SysInfo *si, register __a1 struct SI_LoadAverage *la)
  137. { UWORD bufpos = SysmonBase->sb_LdAvrPtr;
  138.   ULONG total1=0, total2=0, total3=0;
  139.   UWORD count=0;
  140.   UBYTE bufval;
  141.  
  142.   do
  143.   { bufval = SysmonBase->sb_LdAvrBuffer[bufpos];
  144.     if (count < 60) total1 += bufval;
  145.     if (count < 5*60) total2 += bufval;
  146.     total3 += bufval;
  147.  
  148.     if (bufpos == 0) bufpos = 15*60;
  149.   } while (bufpos--, count++, count < 15*60);
  150.  
  151.   la->lavg_fixed.load1 = total1;
  152.   la->lavg_fixed.load2 = total2 / 5;
  153.   la->lavg_fixed.load3 = total3 / 15;
  154. }
  155.  
  156.  
  157. /* This function returns the Process ID associated to a task. Since we don't
  158.    support PIDs, we just return the TCB address. */
  159.  
  160. __asm __saveds LONG GetPid(register __a0 struct SysInfo *si)
  161. { return (LONG)FindTask(NULL);
  162. }
  163.  
  164.  
  165. /* This function returns the Process ID of the task's parent. We don't have
  166.    this information available, so we return an error condition. */
  167.  
  168. __asm __saveds LONG GetPpid(register __a0 struct SysInfo *si)
  169. { return -1L;
  170. }
  171.  
  172.  
  173. /* This function returns the Process group number associated to a task. Since
  174.    we don't support process groups, we return an error condition. */
  175.  
  176. __asm __saveds LONG GetPgrp(register __a0 struct SysInfo *si)
  177. { return -1L;
  178. }
  179.  
  180.  
  181. /* This function returns the so called nice value of a task. We don't support
  182.    such unixisms, so you will get an error */
  183.  
  184. __asm __saveds LONG GetNice(register __a0 struct SysInfo *si, register __d0 LONG which, register __d1 LONG who)
  185. { si->errno = WHICH_EINVAL;
  186.   return -1L;
  187. }
  188.  
  189.  
  190. /* This function sets the so called nice value of a task. We don't support
  191.    such unixisms, so you will get an error */
  192.  
  193. __asm __saveds LONG SetNice(register __a0 struct SysInfo *si, register __d0 LONG which, register __d1 LONG who, register __d2 LONG nice)
  194. { si->errno = WHICH_EINVAL;
  195.   return -1L;
  196. }
  197.  
  198.  
  199. /* This function allows you to request a notification when internal stats have
  200.    been updated. This is currently not supported. */
  201.  
  202. __asm __saveds struct SI_Notify *AddNotify(register __a0 struct SysInfo *si, register __d0 WORD flags, register __d1 LONG safety_limit)
  203. { return NULL;
  204. }
  205.  
  206.  
  207. /* This function removes a notification request. Since notifications are currently
  208.    not supported, there is nothing to do here. */
  209.  
  210. __asm __saveds void RemoveNotify(register __a0 struct SysInfo *si, register __a1 struct SI_Notify *notify)
  211. {
  212. }
  213.  
  214.  
  215. /* This function returns statistics about CPU usage and task switches gathered by the
  216.    server task every second. */
  217.  
  218. __asm __saveds void GetCpuUsage(register __a0 struct SysInfo *si, register __a1 struct SI_CpuUsage *usage)
  219. { struct EClockVal uptime;
  220.   ULONG clockrate, RecentCPU=0;
  221.   int i;
  222.  
  223.   clockrate = ReadEClock(&uptime);
  224.   usage->total_used_cputime = (SysmonBase->sb_CPUTime.ev_hi << 20 | SysmonBase->sb_CPUTime.ev_lo >> 12) / (clockrate >> 12);
  225.   usage->total_elapsed_time = (uptime.ev_hi << 20 | uptime.ev_lo >> 12) / (clockrate >> 12);
  226.   usage->total_csw = SysBase->DispCount;
  227.   usage->voluntary_csw = SysmonBase->sb_VolTSw;
  228.   usage->involuntary_csw = SysmonBase->sb_InvTSw;
  229.  
  230.   Forbid();
  231.   i = (LastRecentCPU == 0 ? CPU_USAGE_RECENT-1 : LastRecentCPU-1);
  232.   usage->used_cputime_lastsec = RecentCPUUsed[i];
  233.   for (i = 0; i < CPU_USAGE_RECENT; i++) RecentCPU += RecentCPUUsed[i];
  234.   usage->recent_used_cputime = RecentCPU / CPU_USAGE_RECENT;
  235.   usage->involuntary_csw_lastsec = LastSecInvTSw;
  236.   usage->voluntary_csw_lastsec = LastSecVolTSw;
  237.   usage->total_csw_lastsec = LastSecDispCount;
  238.   Permit();
  239.  
  240.   usage->used_cputime_lastsec_hz = clockrate;
  241.   usage->recent_used_cputime_hz = clockrate;
  242.   usage->recent_seconds = CPU_USAGE_RECENT;
  243. }
  244.  
  245.  
  246. /* This function returns total CPU usage and task switches for a given task
  247.    as maintained by sysmon.library. No last second or recent information is
  248.    maintained as this would be CPU expensive. */
  249.  
  250. __asm __saveds LONG GetTaskCpuUsage(register __a0 struct SysInfo *si, register __a1 struct SI_TaskCpuUsage *usage, register __a2 struct Task *task)
  251. { struct TaskInfo *tinfo;
  252.   struct EClockVal uptime, elapsed;
  253.   ULONG clockrate;
  254.  
  255.   clockrate = ReadEClock(&uptime) >> 12;
  256.   if (tinfo = smGetTaskInfo(task))
  257.   { usage->total_used_cputime = tinfo->ti_CPUTime.ev_hi << 20 | tinfo->ti_CPUTime.ev_lo >> 12;
  258.     usage->total_used_time_hz = clockrate;
  259.     elapsed = subEClockVal(uptime, tinfo->ti_StartTime);
  260.     usage->total_elapsed_time = (elapsed.ev_hi << 20 | elapsed.ev_lo >> 12) / clockrate;
  261.     usage->total_csw = tinfo->ti_DispCount;
  262.     usage->voluntary_csw = tinfo->ti_VolTSw;
  263.     usage->involuntary_csw = tinfo->ti_InvTSw;
  264.     return 0;
  265.   }
  266.   else
  267.   { si->errno = WHICH_EINVAL;
  268.     return 1;
  269.   }
  270. }
  271.  
  272.  
  273. /* Some functions to add/subtract 64bit EClockVals */
  274.  
  275. static __inline struct EClockVal subEClockVal(struct EClockVal e2, struct EClockVal e1)
  276. { BOOL carry = (e1.ev_lo > e2.ev_lo);
  277.  
  278.   e2.ev_lo -= e1.ev_lo;
  279.   e2.ev_hi -= e1.ev_hi;
  280.   if (carry) e2.ev_hi -= 1;
  281.   return e2;
  282. }
  283.  
  284. static __inline struct EClockVal addEClockVal(struct EClockVal e2, struct EClockVal e1)
  285. { BOOL carry = (e2.ev_lo + e1.ev_lo < e2.ev_lo);
  286.  
  287.   e2.ev_lo += e1.ev_lo;
  288.   e2.ev_hi += e1.ev_hi;
  289.   if (carry) e2.ev_hi += 1;
  290.   return e2;
  291. }
  292.  
  293. /* The SysInfo.server task that will track CPU Usage and task switches every second
  294.    Note that we need to start this via an assembler stub routine as SAS/C libcode
  295.    option assumes the library base to be in A6 at function entry */
  296.  
  297. void __saveds SysInfoServer(void)
  298. { struct MsgPort *timerport;
  299.   struct Library *libbase = (struct Library *)FindTask(NULL)->tc_UserData;
  300.   struct EClockVal LastCPUUsage;
  301.   ULONG LastDispCount, LastVolTSw, LastInvTSw;
  302.  
  303.   Forbid();
  304.   LastCPUUsage = SysmonBase->sb_CPUTime;
  305.   LastDispCount = SysBase->DispCount;
  306.   LastVolTSw = SysmonBase->sb_VolTSw;
  307.   LastInvTSw = SysmonBase->sb_InvTSw;
  308.   Permit();
  309.  
  310.   if (timerport = CreateMsgPort())
  311.   { timereq.tr_node.io_Message.mn_ReplyPort = timerport;
  312.     do
  313.     { timereq.tr_time.tv_secs = 1;
  314.       timereq.tr_time.tv_micro = 0;
  315.       smScheduleWakeUp(&timereq);
  316.       do
  317.       { smHibernate();
  318.       } while (GetMsg(timerport) != (struct Message *)&timereq);
  319.  
  320.       Forbid();        /* Compute and store last second CPU Usage and task switches */
  321.       RecentCPUUsed[LastRecentCPU++] = subEClockVal(SysmonBase->sb_CPUTime,LastCPUUsage).ev_lo;
  322.       LastSecDispCount = SysBase->DispCount - LastDispCount;
  323.       LastSecVolTSw = SysmonBase->sb_VolTSw - LastVolTSw;
  324.       LastSecInvTSw = SysmonBase->sb_InvTSw - LastInvTSw;
  325.       LastCPUUsage = SysmonBase->sb_CPUTime;
  326.       LastDispCount = SysBase->DispCount;
  327.       LastVolTSw = SysmonBase->sb_VolTSw;
  328.       LastInvTSw = SysmonBase->sb_InvTSw;
  329.       Permit();
  330.  
  331.       if (LastRecentCPU == CPU_USAGE_RECENT) LastRecentCPU = 0;
  332.     } while (!ServerExit);
  333.  
  334.     DeleteMsgPort(timerport);
  335.   }
  336.  
  337.   Forbid();
  338.   ServerRunning = FALSE;
  339.   if (libbase->lib_OpenCnt == 0 && libbase->lib_Flags & LIBF_DELEXP)
  340.   { libbase->lib_OpenCnt++;    /* Fake a last opener */
  341.     CloseLibrary(libbase);    /* LibClose will do the delayed expunge */
  342.   }
  343. }
  344.